package com.ruoyi.workflow.service.impl;

import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.workflow.domain.CarPackageAudit;
import com.ruoyi.workflow.domain.ServiceItem;
import com.ruoyi.workflow.domain.vo.TodoTaskVo;
import com.ruoyi.workflow.mapper.CarPackageAuditMapper;
import com.ruoyi.workflow.mapper.ServiceItemMapper;
import com.ruoyi.workflow.service.ITodoTaskService;
import com.ruoyi.workflow.util.ActivitiUtils;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.ruoyi.workflow.constant.AuditConstant.*;

/**
 * 待办任务业务层
 */
@Service
public class TodoTaskServiceImpl implements ITodoTaskService {

    @Autowired
    private CarPackageAuditMapper carPackageAuditMapper;
    @Autowired
    private ServiceItemMapper serviceItemMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private TaskService taskService;


    //查询待办任务：
    /**
     * 出现耦合问题：
     * Activiti7 强制与SpringSecurity依赖 造成当
     * 通过候选人查询任务节点 会 通过 SpringSecurity获取用户名去查询
     * 而设置候选人是 String类型的 userId
     * 解决方案：
     *      把设置候选人的userId的值设置为userName
     * @return
     */
    @Override
    public TableDataInfo todo(Map<String,Object> params) {

        //封装分页表格响应数据
        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setCode(HttpStatus.SUCCESS);
        tableDataInfo.setMsg("查询成功");

        //查询待办任务：
        //1.在security中查询当前登录的用户信息
        String username = SecurityUtils.getUsername();
        //2.根据用户信息的id即user_id去查询所有待办任务
        //接受请求的分页的数据
        PageDomain pageDomain = TableSupport.buildPageRequest();
        //起始索引
        int startIndex = (pageDomain.getPageNum()-1)* pageDomain.getPageSize();
        //3.查询总待办任务总数
        //问题：解决Activiti7强依赖SpringSecurity后是根据 username 去查询候选人或负责人
        //解决：通过userName作为userId的值去查，因为在设置候选人的时候也是把userName作为userId的值
        long taskTotal = taskService.createTaskQuery().taskCandidateOrAssigned(username).count();
        tableDataInfo.setTotal(taskTotal);
        //如果待办任务为0，则直接返回
        if(taskTotal == 0){
            tableDataInfo.setRows(Collections.EMPTY_LIST);
            return tableDataInfo;
        }

        //4.遍历待办任务
        List<TodoTaskVo> todoTaskVoList = new ArrayList<>();
        List<Task> taskList = taskService.createTaskQuery().taskCandidateOrAssigned(username).orderByTaskCreateTime().asc().listPage(startIndex,pageDomain.getPageSize());
        taskList.forEach(item->{
            //4.2 通过流程实例获取businessKey
            String businessKey = ActivitiUtils.getBusinessKeyByRuntimeProcessInstanceId(item.getProcessInstanceId());
            //4.3 根据businessKey查询审核套装的信息
            CarPackageAudit carPackageAudit = carPackageAuditMapper.selectCarPackageAuditById(Long.valueOf(businessKey),params);
            if(carPackageAudit != null){
                //4.4 查询候选人的名字
                List<String> userName = taskService.getIdentityLinksForTask(item.getId()).stream().map(identityLink -> identityLink.getUserId()).collect(Collectors.toList());
                List<String> candidateUsers = sysUserMapper.selectNickNameByBatchUserName(userName);
                TodoTaskVo todoTaskVo = new TodoTaskVo();
                BeanUtils.copyBeanProp(todoTaskVo,carPackageAudit);
                todoTaskVo.setTaskId(item.getId());
                todoTaskVo.setTaskName(item.getName());
                todoTaskVo.setCandidateUserList(candidateUsers);
                todoTaskVoList.add(todoTaskVo);
            }
        });
        tableDataInfo.setRows(todoTaskVoList);
        return tableDataInfo;
    }

    /**
     * 审批任务
     * @param taskId 任务节点ID
     * @param approvalStatus 审批结果
     * @param info 批注信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void approvalTask(String taskId,Integer approvalStatus, String info) {
        //审批时需要做的操作
        //1- 任务：领取/完成/添加批注
        //-----------------------------------
        //获取用户名
        String username = SecurityUtils.getUsername();
        //根据用户名查询任务信息，如果查询不到表示非法操作
        Task currentTask = taskService.createTaskQuery().taskCandidateOrAssigned(username).taskId(taskId).singleResult();
        if(currentTask == null){
            throw new ServiceException("【非法操作】：该用户不是该任务的审核人");
        }
        //根据用户名领取任务
        taskService.claim(taskId,username);
        //根据任务节点获取流程实例Id
        String processInstanceId = currentTask.getProcessInstanceId();
        //根据businessKey关联业务
        String businessKey = ActivitiUtils.getBusinessKeyByRuntimeProcessInstanceId(processInstanceId);
        //添加批注信息
        taskService.addComment(taskId,processInstanceId,info);
        //审批任务的同时设置流程变量【同意还是拒绝】
        taskService.setVariable(taskId,currentTask.getTaskDefinitionKey(),approvalStatus.equals(SI_APPROVAL_STATUS_YES));
        taskService.complete(taskId);

        //此时审核后进入下个节点
        Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        //判断是否有下个节点
        //如果没有下个节点
        if(nextTask == null){
            //审核流程结束，审核结果为通过，把状态修改为 通过
            //修改套餐信息和服务项的 审核状态为 通过
            updateProcessStatus(businessKey,SI_AUDIT_STATUS_PASS);
            return;
        }

        //如果有下个节点
        //如果是拒绝，把状态都调整为 调整申请，并且候选人为发起人【此时才能进行调整申请】
        if (SI_APPROVAL_STATUS_NO.equals(approvalStatus)){
            //把状态调整为 调整申请
            updateProcessStatus(businessKey,SI_AUDIT_STATUS_OUT);
            //设置候选人为发起人
            //根据businessKey 通过套餐信息表中获取creator_user_name
            CarPackageAudit carPackageAudit = carPackageAuditMapper.selectCarPackageAuditById(Long.valueOf(businessKey), Collections.EMPTY_MAP);
            //把username设置为候选人 即 发起人设置为候选人
            taskService.addCandidateUser(nextTask.getId(),carPackageAudit.getCreatorUserName());
            return;
        }

        //如果是同意，设置候选人,状态保持为原状态为：审核中
        //根据任务结点的key查询候选人
        List<SysUser> userList = sysUserMapper.selectUserByNodeIdOrKey(null, nextTask.getTaskDefinitionKey());
        //设置候选人设置的是userId,而我们为了解决代码耦合，是通过userName查找用户，我们把userId的值设置成userName
        userList.forEach(item -> taskService.addCandidateUser(nextTask.getId(), item.getUserName()));
    }


    /**
     * 重新发起申请
     * @param taskId 当前任务节点ID
     * @param packageAuditInfo 发起申请信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reApplyPackageAudit(String taskId, String packageAuditInfo) {
        //1.获取用户名
        String username = SecurityUtils.getUsername();
        //2.根据用户名查询任务信息，如果查询不到表示非法操作
        Task currentTask = taskService.createTaskQuery().taskCandidateOrAssigned(username).taskId(taskId).singleResult();
        if(currentTask == null){
            throw new ServiceException("【非法操作】：该用户不是该任务的审核人");
        }
        //3.根据任务节点获取流程实例Id
        String processInstanceId = currentTask.getProcessInstanceId();
        String businessKey = ActivitiUtils.getBusinessKeyByRuntimeProcessInstanceId(processInstanceId);
        //4.根据businessKey查询审核套餐信息
        CarPackageAudit carPackageAudit = carPackageAuditMapper.selectCarPackageAuditById(Long.valueOf(businessKey), Collections.EMPTY_MAP);
        //5.领取任务, 添加批注信息 ,完成任务 ,并设置候选人
        //5.1 领取任务
        taskService.claim(taskId,SecurityUtils.getUsername());
        //5.2 添加批注信息
        taskService.addComment(taskId,processInstanceId,packageAuditInfo);
        //5.3 完成任务同时设置流程变量
        taskService.complete(taskId);

        //6.此时进入到店长审批的节点，给店长审批设置候选人，因为重新发起申请，候选人可能会改变，所以需要重写设置候选人
        //6.1获取当前任务节点
        Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        //6.2 此时价格已变，所以重新设置流程变量
        //设置流程变量，如果流程变量同名会覆盖之前的值
        taskService.setVariable(nextTask.getId(),"money",carPackageAudit.getServiceItemPrice().doubleValue());
        //7.设置候选人
        //注意：只能到哪个任务节点，才能为该任务结节点设置候选人
        //7.1根据任务结点的key查询候选人
        List<SysUser> userList = sysUserMapper.selectUserByNodeIdOrKey(null, nextTask.getTaskDefinitionKey());
        //7.2设置候选人设置的是userId,而我们为了解决代码耦合，是通过userName查找用户，我们把userId的值设置成userName
        userList.forEach(item -> taskService.addCandidateUser(nextTask.getId(), item.getUserName()));

        //8.把状态修改为 审核中
        //把套餐信息的状态修改为 审核中
        //把服务项的状态修改为 审核中
        updateProcessStatus(businessKey,SI_AUDIT_STATUS_REVIEW);

    }

    /**
     * 更新审核状态
     */
    public void updateProcessStatus(String businessKey, Integer status){
        CarPackageAudit updateCarPackageAudit = new CarPackageAudit();
        //修改状态1---修改套餐信息的审核状态，我的待办是调用了套餐信息表的信息
        updateCarPackageAudit.setId(Long.valueOf(businessKey));
        updateCarPackageAudit.setStatus(status);
        carPackageAuditMapper.updateCarPackageAudit(updateCarPackageAudit);

        //修改状态2---修改服务项信息的审核状态
        CarPackageAudit carPackageAudit = carPackageAuditMapper.selectCarPackageAuditById(Long.valueOf(businessKey), Collections.EMPTY_MAP);
        ServiceItem serviceItem = new ServiceItem();
        serviceItem.setId(carPackageAudit.getServiceItemId());
        serviceItem.setAuditStatus(status);
        serviceItemMapper.updateServiceItem(serviceItem);
    }
}

